/*
 * ExporterToKML.java
 *
 * Created on 02 Octobre 2006, 19:12
 *
 */

package courselog.gpsutils;

import ewe.io.BufferedOutputStream;
import ewe.io.File;
import ewe.io.FileOutputStream;
import ewe.io.IOException;
import ewe.sys.Time;
import ewe.util.Vector;

/**
 * Object to export a track into a GPX format.
 * The object is associated to the TrackRecord or Vector of TrackRecords object to write.
 * @author Christian Rigal
 */
public class ExporterToGPX {
    
    /** Header of XML */
    public final static byte sXSHead[]=
            ("<?xml version=\"1.0\" standalone=\"yes\"?>\n"+
            "<?xml-stylesheet type=\"text/xsl\" href=\"details.xsl\"?>\n").getBytes();
    
    /** Header of GPX */
    public final static byte[] sXSGPXHead=
            ("<gpx\n"+
            "version=\"1.0\"\n"+
            "creator=\"CourseLog - http://courselog.free.fr\"\n"+
            "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"+
            "xmlns=\"http://www.topografix.com/GPX/1/0\"\n"+
            "xmlns:topografix=\"http://www.topografix.com/GPX/Private/TopoGrafix/0/1\"\n"+
            "xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 "+
            "http://www.topografix.com/GPX/1/0/gpx.xsd "+
            "http://www.topografix.com/GPX/Private/TopoGrafix/0/1 "+
            "http://www.topografix.com/GPX/Private/TopoGrafix/0/1/topografix.xsd\">\n").getBytes();
    
    /** End of GPX */
    public final static byte[] sXCGPXHead="</gpx>".getBytes();
    
    /** Header of Track */
    public final static byte[] sXSTrack="<trk>\n".getBytes();
    
    /** close Track */
    public final static byte[] sXCTrack="</trk>\n".getBytes();
    
    /** Header of Name */
    public final static byte[] sXSName="<name>".getBytes();
    
    /** close Name */
    public final static byte[] sXCName="</name>\n".getBytes();
    
    /** Header of CDATA */
    public final static byte[] sXSCData="<![CDATA[".getBytes();
    
    /** close CDATA */
    public final static byte[] sXCData="]]>".getBytes();
    
    /** Header of Number */
    public final static byte[] sXSNum="<Number>".getBytes();
    
    /** close Number */
    public final static byte[] sXCNum="</Number>\n".getBytes();
    
    /** Header of Segment */
    public final static byte[] sXSSegment="<trkseg>\n".getBytes();
    
    /** close Segment */
    public final static byte[] sXCSegment="</trkseg>\n".getBytes();
    
    /** Header of Track Point */
    public final static byte[] sXSPoint="<trkpt lat=\"".getBytes();
    
    /** Intermediate of Track Point */
    public final static byte[] sXIPoint="\" lon=\"".getBytes();
    
    /** Intermediate 2 of Track Point */
    public final static byte[] sXI2Point="\">\n".getBytes();
    
    /** close Point */
    public final static byte[] sXCPoint="</trkpt>\n".getBytes();
    
    /** Header of Elevation */
    public final static byte[] sXSEle="<ele>".getBytes();
    
    /** close Elevation */
    public final static byte[] sXCEle="</ele>\n".getBytes();
    
    /** Header of Time */
    public final static byte[] sXSTime="<time>".getBytes();
    
    /** close Time*/
    public final static byte[] sXCTime="</time>\n".getBytes();
    
    /** Header of Symbol */
    public final static byte[] sXSSym="<sym>Waypoint".getBytes();
    
    /** close Symbol */
    public final static byte[] sXCSym="</sym>\n".getBytes();
    
    /**
     * Track record to convert to KML.
     */
    protected TrackRecord myTr=null;
    
    /**
     * Vector of records to convert to KML tracks.
     */
    protected Vector myVect=null;
    
    /**
     * Creates a new instance of ExporterToGPX.
     * Initialises with a single track.
     * @param track Track to convert.
     */
    public ExporterToGPX(TrackRecord track) {
        myTr=track;
    }
    
    /**
     * Creates a new instance of ExporterToGPX.
     * Initialise with a vector of TrackRecord elements.
     * @param vect Tracks to convert.
     */
    public ExporterToGPX(Vector vect) {
        for(int idx=vect.size()-1; idx>=0; idx--) {
            if(!(vect.elementAt(idx) instanceof TrackRecord)) return;
        }
        if(vect.size()>0) myVect=vect;
    }
    
    /**
     * Export track(s) to a file.
     * <pre>
     * Values are exported in international units :
     * time = UTC
     * alt = m
     * </pre>
     * @param fOut File to write to.
     * @param dataFlag Identification of data to associate.
     */
    public void exportToFile(File fOut, int dataFlag) {
        byte[] buf=new byte[21];
        int lg=0;
        
        try {
            FileOutputStream fWFile= new FileOutputStream(fOut);
            BufferedOutputStream fW= new BufferedOutputStream(fWFile);
            
            fW.write(sXSHead);
            fW.write(sXSGPXHead);
            fW.write(sXSName);
            fW.write("CourseLog tracks".getBytes());
            fW.write(sXCName);
            
            // First folder is the legend
            
            // iterate on tracks
            int nbTracks=1;
            if(myVect!=null) nbTracks=myVect.size();
            TrackRecord trCur=myTr;
            int idxB=0;
            
            for(int idxTrk=0; idxTrk<nbTracks; idxTrk++) {
                
                // If multitrack, get current track
                if(myVect!=null) trCur=(TrackRecord) myVect.elementAt(idxTrk);
                
                fW.write(sXSTrack);
                fW.write(sXSName);
                fW.write("Track number ".getBytes());
                lg=formatIdxTrack(buf, idxTrk);
                fW.write(buf,0,lg);
                fW.write(sXCName);
                
                fW.write(sXSNum);
                fW.write(buf,0,lg); // Number equals <number> ?
                fW.write(sXCNum);
                
                fW.write(sXSSegment);
                
                int nbPt=trCur.vPoints.size();
                for(int idxP=1; idxP<nbPt; idxP++) {
                    
                    // Format new point
                    fW.write(sXSPoint);
                    TrackPoint tPt= (TrackPoint) trCur.vPoints.elementAt(idxP);
                    lg=formatLatOrLon(buf,tPt.lat);
                    fW.write(buf,0,lg); // Latitude
                    fW.write(sXIPoint);
                    lg=formatLatOrLon(buf,tPt.lon);
                    fW.write(buf,0,lg); // Longitude
                    fW.write(sXI2Point);
                    
                    lg=formatAltitude(buf,tPt.altSeaLevel);
                    fW.write(sXSEle);
                    fW.write(buf,0,lg); // Altitude
                    fW.write(sXCEle);
                    
                    lg=formatDate(buf,tPt.measureTime);
                    fW.write(sXSTime);
                    fW.write(buf,1,lg-1); // Time+Date
                    fW.write(sXCTime);
                    
                    fW.write(sXSName);
                    fW.write("point".getBytes());
                    lg=formatIdxTrack(buf, idxP);
                    fW.write(buf,0,lg);
                    fW.write(sXCName);
                    
                    fW.write(sXSSym);
                    fW.write(sXCSym);
                    fW.write(sXCPoint);
                }
                fW.write(sXCSegment);
            }
            
            fW.write(sXCTrack);
            fW.write(sXCGPXHead);
            fW.flush();
            fW.close();
            //fWFile.close();
            
        } catch (SecurityException ex) {
            ex.printStackTrace();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        
    }
    
    /**
     * Format the altitude value in the buffer.
     * @param buf Buffer where to write the formatted value, with 7 elements as a minimum.
     * @param val Altitude value to put in the formatted buffer.
     * @return Numer of characters formatted in the buffer.
     */
    public int formatAltitude(byte[] buf, double val) {
        return Converters.writeDblInBuffer(buf,0,6,1,val);
    }
    
    /**
     * Format a date and time into the buffer <i>buf</i>.
     * Notice that the first character in the buffer is a whitespace 
     * (sign of year value), so skip it if you don't want it.
     * The format is : <pre>yyyy-mm-dd<B>T</B>hh:mi:ss<B>Z</B></pre>
     * @param buf Buffer where to write the formatted value, with 21 elements as a minimum.
     * @param tm Time value to put in the formatted buffer.
     * @return Numer of characters formatted in the buffer.
     */
    public int formatDate(byte[] buf, Time tm) {
        int lg;
        int idx=0;
        lg=Converters.writeIntInBuffer(buf,idx,4, tm.year);
        //System.out.println("year "+tm.year+" lg="+lg);
        idx = lg;
        // in the following, there some trick on idx which
        // stores the start and is used to replace the whitespace by the separator
        lg=Converters.writeIntInBuffer(buf,idx,2, tm.month);
        //System.out.println("month "+tm.month+" lg="+lg);
        buf[idx]='-';
        idx = lg;
        lg=Converters.writeIntInBuffer(buf,idx,2, tm.day);
        //System.out.println("day "+tm.day+" lg="+lg);
        buf[idx]='-';
        idx = lg;
        lg=Converters.writeIntInBuffer(buf,idx,2, tm.hour);
        //System.out.println("hour "+tm.month+" hour="+lg);
        buf[idx]='T';
        idx = lg;
        lg=Converters.writeIntInBuffer(buf,idx,2, tm.minute);
        //System.out.println("minute "+tm.minute+" lg="+lg);
        buf[idx]=':';
        idx = lg;
        lg=Converters.writeIntInBuffer(buf,idx,2, tm.second);
        //System.out.println("second "+tm.second+" lg="+lg);
        buf[idx]=':';
        idx = lg;
        buf[idx]='Z';
        idx++;
        return idx;
    }
    
    /**
     * Format the speed value in the buffer.
     * @param buf Buffer where to write the formatted value, with 6 elements as a minimum.
     * @param val Speed value to put in the formatted buffer.
     * @return Numer of characters formatted in the buffer.
     */
    public int formatSpeed(byte[] buf, double val) {
        return Converters.writeDblInBuffer(buf,0,5,1,val);
    }
    
    /**
     * Format latitude or longitude value.
     * @param buf Buffer where to write the formatted value, with 11 elements as a minimum.
     * @param val Lat(Long)itude value to put in the formatted buffer.
     * @return Numer of characters formatted in the buffer.
     */
    public int formatLatOrLon(byte[] buf, double val) {
        return Converters.writeDblInBuffer(buf,0,10,6,val);
    }
    
    /**
     * Format an index into the buffer. used for point or track index.
     * @param buf Buffer where to write the formatted value, with 5 elements as a minimum.
     * @param idx index value to put in the formatted buffer.
     * @return Numer of characters formatted in the buffer.
     */
    public int formatIdxTrack(byte[] buf, int idx) {
        return Converters.writeIntInBuffer(buf,0,4,idx);
    }
    
}